﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using SecurityFoundation;
using SecurityFoundation.Entities;
using System.Runtime.Serialization;
using System.ServiceModel;




namespace SecurityAgent
{
    [SecurityExceptionHandler(AttributePriority=99)]
    public class FieldSecurityService : SecurityAgent.IFieldSecurityService
    {
        public bool IsFieldAuthorized(string applicationKey, string userToken, string objectName, string fieldName, int operation )
        {
            int level = GetFieldAuthorization(applicationKey, userToken, objectName, fieldName);

            int result = level & operation;
            return result > 0;
        }

        public int GetFieldAuthorization(string applicationKey, string userToken, string objectName, string fieldName)
        {
            int level = 0;

            try
            {
                String userName = Repository.ValidUserToken(new Guid(applicationKey), userToken);

                ApplicationDefination app = Repository.GetApplicationDefinition(new Guid(applicationKey));
                ObjectDefinition obj = Repository.GetObjectDefinition(app.ApplicationKey, objectName);

                if (obj.FieldLevelSecurity == (int)ObjectSecurityRequirement.NotRequired) 
                    return (int) SecurityAccessMode.AllowAll;    // 0x1111

                RoleProviderService roleService = new RoleProviderService();

                bool bIsOwned = false;
                if (!String.IsNullOrEmpty(obj.OwnerRole) && roleService.IsUserInRole(applicationKey, userToken, String.Empty, obj.OwnerRole))
                {
                    bIsOwned = true;
                }

                FieldAcl[] acls = Repository.GetFieldAcl(app.ApplicationKey, objectName, fieldName);

                List<FieldAcl> validAcls = new List<FieldAcl>();

                foreach (FieldAcl acl in acls)
                {
                    if (roleService.IsUserInRole(applicationKey, userToken, String.Empty, acl.RoleName))
                    {
                        validAcls.Add(acl);
                    }
                }

                if (obj.FieldLevelSecurity == (int)ObjectSecurityRequirement.Optional)
                {
                    level = (int)SecurityAccessMode.AllowAll;         // allowed by default

                    if (validAcls.Count > 0)
                    {
                        foreach (FieldAcl acl in validAcls)
                        {
                            if (bIsOwned)
                            {
                                if (!acl.OwnAllowRead) level = level & (int)SecurityAccessMode.DenyRead;
                                if (!acl.OwnAllowUpdate) level = level & (int)SecurityAccessMode.DenyUpdate;
                            }
                            else
                            {
                                if (!acl.AllAllowRead) level = level & (int)SecurityAccessMode.DenyRead;
                                if (!acl.AllAllowUpdate) level = level & (int)SecurityAccessMode.DenyUpdate;
                            }
                        }
                    }
                }
                else    // SecurityRequirement.Required
                {
                    level = (int)SecurityAccessMode.DenyAll;

                    if (validAcls.Count > 0)
                    {
                        foreach (FieldAcl acl in validAcls)
                        {
                            if (bIsOwned)
                            {
                                if (acl.OwnAllowRead) level = level | (int)SecurityAccessMode.AllowRead;
                                if (acl.OwnAllowUpdate) level = level | (int)SecurityAccessMode.AllowUpdate;
                            }
                            else
                            {
                                if (acl.AllAllowRead) level = level | (int)SecurityAccessMode.AllowRead;
                                if (acl.AllAllowUpdate) level = level | (int)SecurityAccessMode.AllowUpdate;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }

            return level;
        }

        public FieldClaim GetFieldClaim(string applicationKey, string userToken, string objectName, string fieldName)
        {
            try
            {
                String userName = Repository.ValidUserToken(new Guid(applicationKey), userToken);

                ApplicationDefination app = Repository.GetApplicationDefinition(new Guid(applicationKey));
                ObjectDefinition obj = Repository.GetObjectDefinition(app.ApplicationKey, objectName);

                if (obj == null)
                {
                    throw new Exception("Invalid Object");
                }


                FieldDefinition fld = Repository.GetFieldDefinition(app.ApplicationKey, objectName, fieldName);

                if (fld == null)
                {
                    throw new Exception("Invalid Field");
                }

                int level = GetFieldAuthorization(applicationKey, userToken, objectName, fieldName);

                FieldClaim ret = new FieldClaim();

                ret.ObjectKey = obj.ObjectId;
                ret.ObjectName = obj.ObjectName;

                ret.FieldKey = fld.FieldId;
                ret.FieldName = fld.FieldName;

                ret.FieldDataType = fld.FieldDataType;
                ret.FieldAccess = new AccessLevel();
                ret.FieldAccess.ObjectKey = fld.FieldId;
                ret.FieldAccess.AllowRead = ((level & (int)SecurityAccessMode.AllowRead) > 0);
                ret.FieldAccess.AllowCreate = ((level & (int)SecurityAccessMode.AllowCreate) > 0);
                ret.FieldAccess.AllowDelete = ((level & (int)SecurityAccessMode.AllowDelete) > 0);
                ret.FieldAccess.AllowUpdate = ((level & (int)SecurityAccessMode.AllowUpdate) > 0);

                return ret;

            }
            catch (Exception ex)
            {
                throw new FaultException(ex.Message);
            }
        }

        private string MakeFieldSettingKey(string objectName, string fieldName)
        {
            return "F_" + objectName.Trim() + "_" + fieldName.Trim();
        }

        public string GetFieldSettingValue(string applicationKey, string userToken, string objectName, string fieldName, string setting)
        {
            if (!IsFieldAuthorized(applicationKey, userToken, objectName, fieldName, (int)SecurityAccessMode.AllowRead))
            {
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault { Application = applicationKey, Object = objectName, Operation = "Read Settings" }, new FaultReason("Unauthorized access attempt recorded"));
            }


            try
            {
                UserProfileData profile = Repository.GetUserProfileData(new Guid(applicationKey), MakeFieldSettingKey(objectName, fieldName));
                string ret = string.Empty;
                if (profile == null) return null;

                XDocument xdoc = XDocument.Parse(profile.ProfileData);

                var query = (from en in xdoc.Root.Elements(setting)
                             select en).FirstOrDefault();

                if (query != null)
                {
                    ret = query.Value;
                }

                return ret;
            }
            catch (Exception ee)
            {
                throw new FaultException(ee.Message);
            }

        }

        public string GetFieldSettings(string applicationKey, string userToken, string objectName, string fieldName)
        {
            if (!IsFieldAuthorized(applicationKey, userToken, objectName, fieldName, (int)SecurityAccessMode.AllowRead))
            {
                throw new FaultException<UnauthorizedAccessFault>(new UnauthorizedAccessFault { Application = applicationKey, Object = objectName, Operation = "Read Settings" }, new FaultReason("Unauthorized access attempt recorded"));
            }

            try
            {

                UserProfileData profile = Repository.GetUserProfileData(new Guid(applicationKey), MakeFieldSettingKey(objectName, fieldName));
                string ret = string.Empty;
                if (profile == null) return null;
                else
                    return profile.ProfileData;
            }
            catch (Exception ee)
            {
                throw new FaultException(ee.Message);
            }

        }
    }
}
